<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  <title>前端中的MVC | Storm</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="前端中的MVC需求:通过AJAX获取数据，展示一个图书的书名，数量，并且用户可以修改数量，并上传到服务器。 ###1.意大利面条写法 1&amp;lt;div id=&quot;app&quot;&amp;gt; &amp;lt;/div&amp;gt; 1234567891011121314151617181920212223242526272829303132333435363738394041424344/*因为没有后台，默认bookdata">
<meta name="keywords" content="JavaScript">
<meta property="og:type" content="article">
<meta property="og:title" content="前端中的MVC">
<meta property="og:url" content="https://storm4542.github.io/archives/5983b51f.html">
<meta property="og:site_name" content="Storm">
<meta property="og:description" content="前端中的MVC需求:通过AJAX获取数据，展示一个图书的书名，数量，并且用户可以修改数量，并上传到服务器。 ###1.意大利面条写法 1&amp;lt;div id=&quot;app&quot;&amp;gt; &amp;lt;/div&amp;gt; 1234567891011121314151617181920212223242526272829303132333435363738394041424344/*因为没有后台，默认bookdata">
<meta property="og:locale" content="zh-CN">
<meta property="og:updated_time" content="2018-08-25T09:08:27.161Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="前端中的MVC">
<meta name="twitter:description" content="前端中的MVC需求:通过AJAX获取数据，展示一个图书的书名，数量，并且用户可以修改数量，并上传到服务器。 ###1.意大利面条写法 1&amp;lt;div id=&quot;app&quot;&amp;gt; &amp;lt;/div&amp;gt; 1234567891011121314151617181920212223242526272829303132333435363738394041424344/*因为没有后台，默认bookdata">
  
    <link rel="alternative" href="https://storm4542.github.io/atom.xml" title="Storm" type="application/atom+xml">
  
  
    <link rel="icon" href="https://storm4542.github.io/favicon.png">
  
  <link href="static/css/css.css" rel="stylesheet" type="text/css">
  <link rel="stylesheet" href="static/css/style.css">
  

</head></html>
<body>
  <div id="container">
    <div id="wrap">
      <div class="outer">
        <section id="main"><article id="post-MVC" class="article article-type-post" itemscope="" itemprop="blogPost">
  
  <div class="article-inner">
    
      <header class="article-header">
        
  
    <h1 class="article-title" itemprop="name">
      前端中的MVC
    </h1>
  

      </header>
    
    <div class="article-meta">
      <a href="" class="article-date">
  <time datetime="2018-08-24T09:16:35.000Z" itemprop="datePublished">2018-08-24</time>
</a>
      
    </div>
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="前端中的MVC"><a href="#前端中的MVC" class="headerlink" title="前端中的MVC"></a>前端中的MVC</h2><p><strong>需求:通过AJAX获取数据，展示一个图书的书名，数量，并且用户可以修改数量，并上传到服务器。</strong></p>
<p>###1.意大利面条写法</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span>&gt;</span> <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*因为没有后台，默认bookdata就是从服务器传来的数据，由fethDb获取，saveDb更新 */</span></span><br><span class="line"><span class="keyword">var</span> bookData = &#123;</span><br><span class="line">    <span class="string">"id"</span>: <span class="string">"1"</span>,</span><br><span class="line">    <span class="string">"name"</span>: <span class="string">"javascript高程"</span>,</span><br><span class="line">    <span class="string">"number"</span>: <span class="number">2</span> <span class="comment">//数量</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fetchDb</span>(<span class="params"></span>) </span>&#123; <span class="comment">//获取数据</span></span><br><span class="line">    <span class="keyword">return</span> bookData;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">saveDb</span>(<span class="params"></span>) </span>&#123; <span class="comment">//更新数据</span></span><br><span class="line">    <span class="keyword">return</span> bookData;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*我们需要把内容放到HTML里，所以需要有个模板 */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> template = <span class="string">`</span></span><br><span class="line"><span class="string">        书名&lt;span id="name"&gt;《__name__》&lt;/span&gt;</span></span><br><span class="line"><span class="string">        数量&lt;span id="number"&gt; __number__&lt;/span&gt;</span></span><br><span class="line"><span class="string">        &lt;div&gt;</span></span><br><span class="line"><span class="string">        &lt;button id="add"&gt;加一&lt;/button&gt;</span></span><br><span class="line"><span class="string">        &lt;button id="minus"&gt;减一&lt;/button&gt;</span></span><br><span class="line"><span class="string">        &lt;/div&gt;</span></span><br><span class="line"><span class="string">        `</span></span><br><span class="line"><span class="comment">/*将我们的数据，放入模板*/</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">showBook</span>(<span class="params">result</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">var</span> html = template.replace(<span class="string">'__name__'</span>, result.name).replace(<span class="string">'__number__'</span>, result.number);</span><br><span class="line">    $(<span class="string">'#app'</span>).html(html)</span><br><span class="line">&#125;</span><br><span class="line">showBook(fetchDb()) <span class="comment">//渲染模板</span></span><br><span class="line"><span class="comment">/*写加减数量的方法 */</span>     </span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params"></span>) </span>&#123; </span><br><span class="line">    bookData.number+=<span class="number">1</span> </span><br><span class="line">    <span class="keyword">var</span> newdata =  saveDb(); <span class="comment">//假设更新了，并从服务器获取了修改后的新数据</span></span><br><span class="line">    $(<span class="string">'#number'</span>).text( newdata.number)</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line">$(<span class="string">'#add'</span>).on(<span class="string">'click'</span>,add)</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">minus</span>(<span class="params"></span>) </span>&#123; </span><br><span class="line">    bookData.number-=<span class="number">1</span></span><br><span class="line">    <span class="keyword">var</span> newdata =  saveDb();</span><br><span class="line">    $(<span class="string">'#number'</span>).text( newdata.number)</span><br><span class="line">    </span><br><span class="line">&#125;</span><br><span class="line">$(<span class="string">'#minus'</span>).on(<span class="string">'click'</span>,minus)</span><br></pre></td></tr></table></figure>
<p><em>这种代码写法叫做意大利面条式代码，表示代码长短不一，很杂乱，如果我想要读懂这段代码，必须从开头读起。</em></p>
<h2 id="2-MVC"><a href="#2-MVC" class="headerlink" title="2.MVC"></a>2.MVC</h2><p><strong>一些程序员想出了解决办法</strong></p>
<p>一些程序员通过自己的总结，发现这些代码总是可以分成三类：</p>
<ol>
<li>专门操作远程数据的代码（fetchDb 和 saveDb 等等）</li>
<li>专门呈现页面元素的代码（innerHTML 等等）</li>
<li>其他控制逻辑的代码（点击某按钮之后做啥的代码）</li>
</ol>
<p>为什么分成这三类呢？因为我们前端抄袭了后端的分类思想，后端代码也经常分为三类：</p>
<ol>
<li>专门操作 MySQL 数据库的代码</li>
<li>专门渲染 HTML 的代码</li>
<li>其他控制逻辑的代码（用户请求首页之后去读数据库，然后渲染 HTML 作为响应等等）</li>
</ol>
<p>这些思路经过慢慢的演化，最终被广大程序员完善为 MVC 思想。</p>
<ol>
<li>M 专门负责数据</li>
<li>V 专门负责表现</li>
<li>C 负责其他逻辑</li>
</ol>
<p>如果我们来反思一下，会发现这个分类是无懈可击的：</p>
<ol>
<li>每个网页都有数据</li>
<li>每个网页都有表现（具体为 HTML）</li>
<li>每个网页都有其他逻辑</li>
</ol>
<p>于是乎，MVC 成了经久不衰的设计模式（设计模式就是「套路」的意思）</p>
<p><strong>我们改写一下上面的代码</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*创建一个model层 负责存储数据、请求数据、更新数据*/</span></span><br><span class="line"><span class="comment">/*因为没有后台，默认bookdata就是从服务器传来的数据，由fethDb获取，saveDb更新 */</span></span><br><span class="line"><span class="keyword">let</span> model = &#123;</span><br><span class="line">    bookData: &#123;</span><br><span class="line">        <span class="string">"id"</span>: <span class="string">"1"</span>,</span><br><span class="line">        <span class="string">"name"</span>: <span class="string">"javascript高程"</span>,</span><br><span class="line">        <span class="string">"number"</span>: <span class="number">2</span> <span class="comment">//数量</span></span><br><span class="line">    &#125;,</span><br><span class="line">    fetchDb() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.bookData;</span><br><span class="line">    &#125;,</span><br><span class="line">    saveDb() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.bookData;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">/*view层，渲染视图*/</span></span><br><span class="line"><span class="keyword">let</span> view = &#123;</span><br><span class="line">    el: <span class="string">'#app'</span>,</span><br><span class="line">    template : <span class="string">`</span></span><br><span class="line"><span class="string">    书名&lt;span id="name"&gt;《__name__》&lt;/span&gt;</span></span><br><span class="line"><span class="string">    数量&lt;span id="number"&gt; __number__&lt;/span&gt;</span></span><br><span class="line"><span class="string">    &lt;div&gt;</span></span><br><span class="line"><span class="string">    &lt;button id="add"&gt;加一&lt;/button&gt;</span></span><br><span class="line"><span class="string">    &lt;button id="minus"&gt;减一&lt;/button&gt;</span></span><br><span class="line"><span class="string">    &lt;/div&gt;</span></span><br><span class="line"><span class="string">    `</span>,</span><br><span class="line">    render(result) &#123; <span class="comment">//此处将showbook改名为render</span></span><br><span class="line">        <span class="keyword">var</span> html = <span class="keyword">this</span>.template.replace(<span class="string">'__name__'</span>, result.name).replace(<span class="string">'__number__'</span>, result.number);</span><br><span class="line">        $(<span class="string">'#app'</span>).html(html)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*控制器，负责调度 model 和 view */</span></span><br><span class="line"><span class="keyword">let</span> controller = &#123;</span><br><span class="line">    init(&#123;model,view&#125;)&#123;</span><br><span class="line">        <span class="keyword">this</span>.model = model</span><br><span class="line">        <span class="keyword">this</span>.view = view</span><br><span class="line">        <span class="keyword">this</span>.view.render(model.fetchDb()) <span class="comment">//渲染模板</span></span><br><span class="line">        <span class="keyword">this</span>.bindEvents()</span><br><span class="line">    &#125;,</span><br><span class="line">    events:[</span><br><span class="line">        &#123;<span class="attr">type</span>:<span class="string">'click'</span>,<span class="attr">selector</span>:<span class="string">'#add'</span>,<span class="attr">fn</span>:<span class="string">'add'</span>&#125;,</span><br><span class="line">        &#123;<span class="attr">type</span>:<span class="string">'click'</span>,<span class="attr">selector</span>:<span class="string">'#minus'</span>,<span class="attr">fn</span>:<span class="string">'minus'</span>&#125;,</span><br><span class="line">    ],</span><br><span class="line">    bindEvents()&#123;</span><br><span class="line">        <span class="keyword">this</span>.events.map(<span class="function">(<span class="params">e</span>)=&gt;</span>&#123;</span><br><span class="line">            $(<span class="keyword">this</span>.view.el).on(e.type,e.selector,<span class="keyword">this</span>[e.fn].bind(<span class="keyword">this</span>)) <span class="comment">//事件绑定 注意this需要使用外面的this</span></span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;,</span><br><span class="line">    add() &#123;</span><br><span class="line">        model.bookData.number += <span class="number">1</span></span><br><span class="line">        <span class="keyword">var</span> newdata = model.saveDb(); <span class="comment">//假设更新了，并从服务器获取了修改后的新数据</span></span><br><span class="line">        $(<span class="string">'#number'</span>).text(newdata.number)</span><br><span class="line"></span><br><span class="line">    &#125;,</span><br><span class="line">    minus() &#123;</span><br><span class="line">        model.bookData.number -= <span class="number">1</span></span><br><span class="line">        <span class="keyword">var</span> newdata = model.saveDb();</span><br><span class="line">        $(<span class="string">'#number'</span>).text(newdata.number)</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">controller.init(&#123;model,view&#125;)</span><br></pre></td></tr></table></figure>
<h2 id="3-模板代码-也就是类"><a href="#3-模板代码-也就是类" class="headerlink" title="3.模板代码(也就是类)"></a>3.模板代码(也就是类)</h2><p>一个页面或模块只需要 model view controller 三个对象<br>第二个页面就需要再来 model2 view2 controller2 三个对象<br>第三个页面就需要再来 model3 view3 controller3 三个对象<br>……<br>第N个页面就需要再来 modelN viewN controllerN 三个对象</p>
<p>你每次写一个 model 都要写很类似的代码<br>你每次写一个 view 都要写很类似的代码<br>你每次写一个 controller 都要写很类似的代码</p>
<p>为什么不利用模板代码（俗称面向对象）把重复的代码写到一个类呢（JS里面就是把「共有属性」放到原型里）</p>
<p>代码如下</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*创建一个model calss 负责存储数据、请求数据、更新数据*/</span></span><br><span class="line"><span class="comment">/*因为没有后台，默认bookdata就是从服务器传来的数据，由fethDb获取，saveDb更新 */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Model</span></span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>(options)&#123;</span><br><span class="line">        <span class="keyword">this</span>.data = options.data || &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    fetchDb()&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.data</span><br><span class="line">    &#125;</span><br><span class="line">    saveDb()&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.data</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> model = <span class="keyword">new</span> Model(&#123;</span><br><span class="line">    data: &#123;</span><br><span class="line">        <span class="string">"id"</span>: <span class="string">"1"</span>,</span><br><span class="line">        <span class="string">"name"</span>: <span class="string">"javascript高程"</span>,</span><br><span class="line">        <span class="string">"number"</span>: <span class="number">2</span> <span class="comment">//数量</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">/*view class，渲染视图*/</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">View</span></span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>(options)&#123;</span><br><span class="line">        <span class="keyword">this</span>.el = options.el</span><br><span class="line">        <span class="keyword">this</span>.template = options.template</span><br><span class="line">    &#125;</span><br><span class="line">    render(result)&#123;</span><br><span class="line">        <span class="keyword">var</span> html = <span class="keyword">this</span>.template.replace(<span class="string">'__name__'</span>, result.name).replace(<span class="string">'__number__'</span>, result.number);</span><br><span class="line">        $(<span class="keyword">this</span>.el).html(html)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> view = <span class="keyword">new</span> View(&#123;</span><br><span class="line">    el:<span class="string">'#app'</span>,</span><br><span class="line">    template:<span class="string">`</span></span><br><span class="line"><span class="string">    书名&lt;span id="name"&gt;《__name__》&lt;/span&gt;</span></span><br><span class="line"><span class="string">    数量&lt;span id="number"&gt; __number__&lt;/span&gt;</span></span><br><span class="line"><span class="string">    &lt;div&gt;</span></span><br><span class="line"><span class="string">    &lt;input type="text"&gt;</span></span><br><span class="line"><span class="string">    &lt;button id="add"&gt;加一&lt;/button&gt;</span></span><br><span class="line"><span class="string">    &lt;button id="minus"&gt;减一&lt;/button&gt;</span></span><br><span class="line"><span class="string">    &lt;/div&gt;</span></span><br><span class="line"><span class="string">    `</span>,</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">/*控制器 class，负责调度 model 和 view */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Controller</span></span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>(&#123;view ,model , events , init , ...rest&#125;)&#123;</span><br><span class="line">        <span class="keyword">this</span>.view = view</span><br><span class="line">        <span class="keyword">this</span>.model = model</span><br><span class="line">        <span class="keyword">this</span>.events = events</span><br><span class="line">        <span class="built_in">Object</span>.assign(<span class="keyword">this</span>,rest)</span><br><span class="line">        <span class="keyword">this</span>.bindEvents()</span><br><span class="line">        <span class="comment">//this.view.render(this.model.fetchDb())</span></span><br><span class="line">        init.apply()</span><br><span class="line">    &#125;</span><br><span class="line">    bindEvents()&#123;</span><br><span class="line">        <span class="keyword">this</span>.events.map(<span class="function">(<span class="params">e</span>)=&gt;</span>&#123;</span><br><span class="line">            $(<span class="keyword">this</span>.view.el).on(e.type,e.selector,<span class="keyword">this</span>[e.fn].bind(<span class="keyword">this</span>)) <span class="comment">//事件绑定 注意this需要使用外面的this</span></span><br><span class="line">        &#125;)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> controller = <span class="keyword">new</span> Controller(&#123;</span><br><span class="line">    view : view,</span><br><span class="line">    model : model,</span><br><span class="line">    events:[</span><br><span class="line">        &#123;<span class="attr">type</span>:<span class="string">'click'</span>,<span class="attr">selector</span>:<span class="string">'#add'</span>,<span class="attr">fn</span>:<span class="string">'add'</span>&#125;,</span><br><span class="line">        &#123;<span class="attr">type</span>:<span class="string">'click'</span>,<span class="attr">selector</span>:<span class="string">'#minus'</span>,<span class="attr">fn</span>:<span class="string">'minus'</span>&#125;,</span><br><span class="line">    ],</span><br><span class="line">    init()&#123;</span><br><span class="line">        <span class="keyword">this</span>.view.render(<span class="keyword">this</span>.model.fetchDb());</span><br><span class="line">    &#125;,</span><br><span class="line">    add() &#123;</span><br><span class="line">        <span class="keyword">this</span>.model.data.number += <span class="number">1</span></span><br><span class="line">        <span class="keyword">var</span> newdata = <span class="keyword">this</span>.model.saveDb(); <span class="comment">//假设更新了，并从服务器获取了修改后的新数据</span></span><br><span class="line">        <span class="keyword">this</span>.view.render(<span class="keyword">this</span>.model.saveDb());</span><br><span class="line"></span><br><span class="line">    &#125;,</span><br><span class="line">    minus() &#123;</span><br><span class="line">        <span class="keyword">this</span>.model.data.number -= <span class="number">1</span></span><br><span class="line">        <span class="keyword">if</span>(<span class="keyword">this</span>.model.data.number &lt;<span class="number">0</span>)&#123;</span><br><span class="line">            <span class="keyword">this</span>.model.data.number = <span class="number">0</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">var</span> newdata = <span class="keyword">this</span>.model.saveDb();</span><br><span class="line">        <span class="keyword">this</span>.view.render(<span class="keyword">this</span>.model.saveDb());</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p><strong>这么写，有一个bug ，每次更新数据，我们都要用 innerHtml 替换 #app里的东西，里面如果有 input 标签，用户的数据就无法保存</strong></p>
<h2 id="4-Vue的双向绑定"><a href="#4-Vue的双向绑定" class="headerlink" title="4.Vue的双向绑定"></a>4.Vue的双向绑定</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*创建一个model calss 负责存储数据、请求数据、更新数据*/</span></span><br><span class="line"><span class="comment">/*因为没有后台，默认bookdata就是从服务器传来的数据，由fethDb获取，saveDb更新 */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Model</span> </span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>(options) &#123;</span><br><span class="line">        <span class="keyword">this</span>.data = options.data || &#123;&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    fetchDb() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.data</span><br><span class="line">    &#125;</span><br><span class="line">    saveDb() &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.data</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> model = <span class="keyword">new</span> Model(&#123;</span><br><span class="line">    data: &#123;</span><br><span class="line">        <span class="string">"id"</span>: <span class="string">"1"</span>,</span><br><span class="line">        <span class="string">"name"</span>: <span class="string">"javascript高程"</span>,</span><br><span class="line">        <span class="string">"number"</span>: <span class="number">2</span> <span class="comment">//数量</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">/*把 View 换成 VUE*/</span></span><br><span class="line"><span class="keyword">var</span> view = <span class="keyword">new</span> Vue(&#123;</span><br><span class="line">    el: <span class="string">'#app'</span>,</span><br><span class="line">    data: &#123;</span><br><span class="line">        book: &#123;</span><br><span class="line">            id:<span class="literal">null</span>,</span><br><span class="line">            name: <span class="string">'未命名'</span>,</span><br><span class="line">            number: <span class="number">0</span>,</span><br><span class="line">        &#125;,</span><br><span class="line">        n: <span class="number">100</span></span><br><span class="line"></span><br><span class="line">    &#125;,</span><br><span class="line">    template: <span class="string">`</span></span><br><span class="line"><span class="string">    &lt;div&gt;</span></span><br><span class="line"><span class="string">    书名&lt;span id="name" &gt;&#123;&#123; book.name &#125;&#125;&lt;/span&gt;</span></span><br><span class="line"><span class="string">    数量&lt;span id="number" &gt; &#123;&#123;book.number&#125;&#125;&lt;/span&gt;</span></span><br><span class="line"><span class="string">    &lt;div&gt;</span></span><br><span class="line"><span class="string">    &lt;input type="text" v-model = 'n'&gt;</span></span><br><span class="line"><span class="string">    &lt;button id="add" v-on:click = 'add'&gt;加一&lt;/button&gt;</span></span><br><span class="line"><span class="string">    &lt;button id="minus" v-on:click = 'minus'&gt;减一&lt;/button&gt;</span></span><br><span class="line"><span class="string">    &lt;/div&gt;</span></span><br><span class="line"><span class="string">    &lt;/div&gt;</span></span><br><span class="line"><span class="string">    `</span>,</span><br><span class="line">    created() &#123;</span><br><span class="line">        </span><br><span class="line">        view.book= model.fetchDb()</span><br><span class="line">       </span><br><span class="line">    &#125;,</span><br><span class="line">    methods: &#123;</span><br><span class="line">        add() &#123;</span><br><span class="line">            model.data.number += <span class="number">1</span></span><br><span class="line">            <span class="built_in">console</span>.log(model.saveDb().number)</span><br><span class="line">            <span class="keyword">this</span>.book.number = model.saveDb().number; <span class="comment">//假设更新了，并从服务器获取了修改后的新数据</span></span><br><span class="line"></span><br><span class="line">        &#125;,</span><br><span class="line">        minus() &#123;</span><br><span class="line">            model.data.number -= <span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> (model.data.number &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                model.data.number = <span class="number">0</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">this</span>.book.number = model.saveDb().number;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>Vue 代替了 View，这就是 Vue 的名字和其读音的来历。</p>
<p>Vue 的双向绑定（也是 Angular 的双向绑定）有这些功能：</p>
<ol>
<li>只要 JS 改变了 view.number 或 view.name 或 view.n （注意 Vue 把 data 里面的 number、name 和 n 放到了 view 上面，没有 view.data 这个东西）， HTML 就会局部更新</li>
<li>只要用户在 input 里输入了值，JS 里的 view.n 就会更新。</li>
</ol>
<p>这就像双向绑定：JS 数据与页面元素互相绑定。</p>
<h3 id="Vue的双向绑定原理"><a href="#Vue的双向绑定原理" class="headerlink" title="Vue的双向绑定原理"></a>Vue的双向绑定原理</h3><p>使用了getter setter，缺点是无法监听不存在的属性</p>
<p>原理：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> data = &#123;<span class="attr">name</span>:<span class="string">'aaa'</span>&#125;</span><br><span class="line"><span class="keyword">var</span> _name = <span class="string">'zty'</span></span><br><span class="line"><span class="built_in">Object</span>.defineProperty(data,<span class="string">'name'</span>,&#123;</span><br><span class="line">  get()&#123;<span class="keyword">return</span> _name&#125;</span><br><span class="line">  set(value)&#123; _name = value ; <span class="built_in">console</span>.log(<span class="string">'更新了name'</span>)&#125; <span class="comment">//在这里做一个拦截，我们就可以知道用户修改了某个值，比如说input</span></span><br><span class="line">&#125;)</span><br><span class="line"><span class="built_in">console</span>.log(data.name)</span><br></pre></td></tr></table></figure>
<p>但是假如之前没有在 <code>data</code>里声明，，vue就无法监听了</p>
<p>强制监听</p>
<p><code>vm.$set()</code></p>
<p>向响应式对象中添加一个属性，并确保这个新属性同样是响应式的，且触发视图更新。它必须用于向响应式对象上添加新属性，因为 Vue 无法探测普通的新增属性 (比如 <code>this.myObject.newProperty = &#39;hi&#39;</code>)</p>
<p><a href="javascript:;" rel="external nofollow noopener noreferrer" target="_blank">vm-set</a></p>
<h3 id="模拟上面的set-和-get"><a href="#模拟上面的set-和-get" class="headerlink" title="模拟上面的set 和 get"></a>模拟上面的set 和 get</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">X</span></span>&#123;</span><br><span class="line">    <span class="keyword">constructor</span>(&#123;data&#125;)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="keyword">let</span> key <span class="keyword">in</span> data)&#123;</span><br><span class="line">            <span class="built_in">Object</span>.defineProperty(<span class="keyword">this</span>, key, &#123;</span><br><span class="line">                get ()&#123;</span><br><span class="line">                    <span class="keyword">return</span> data[key]</span><br><span class="line">                &#125;,</span><br><span class="line">                set (value)&#123;</span><br><span class="line">                    <span class="built_in">console</span>.log(<span class="string">'有人修改了'</span> + key)</span><br><span class="line">                    data[key] = value</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> view = <span class="keyword">new</span> X(&#123;</span><br><span class="line">    data: &#123;</span><br><span class="line">        name: <span class="string">'frank'</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(view.name === <span class="string">'frank'</span>) <span class="comment">// 输出 true</span></span><br><span class="line">view.name = <span class="string">'jack'</span> <span class="comment">// 输出「有人修改了 name」</span></span><br></pre></td></tr></table></figure>

      

      
        
    </div>
  </div>
  
    
<nav id="article-nav">
  
    <a href="4cf7e60f.html" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Newer</strong>
      <div class="article-nav-title">
        
          JavaScript里的函数是什么
        
      </div>
    </a>
  
  
    <a href="3404a532.html" id="article-nav-older" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Older</strong>
      <div class="article-nav-title">重构</div>
    </a>
  
</nav>

  
</article>


<section id="comments">
  <link rel="stylesheet" href="static/css/default.css">
  <script src="static/js/gitment.browser.js"></script>
  <script>
    var gitment = new Gitment({
      owner: 'your github account',
      repo: 'your repo',
      oauth: {
        client_id: 'your client_id',
        client_secret: 'your client_secret',
      },
    })
    gitment.render('comments')
  </script>
</section>

</section>
        <aside id="sidebar">
  <nav class="menus">
  	<ul>
  		<li><a href="index.html"><i class="icon icon-home"></i></a></li>
  		
			<li><a href="index1.html"><i class="icon icon-fenlei"></i></a></li>
  		
  		
			<li><a href="index2.html"><i class="icon icon-tag"></i></a></li>
  		
  		
  			<li><a href="javascript:;" target="_blank" rel="external nofollow noopener noreferrer"><i class="icon icon-github"></i></a></li>
  		
  	</ul>
  </nav>
  <a id="go-top" href="#"><i class="icon icon-up"></i></a>
</aside>

      </div>
      <footer id="footer">
  
	<div id="footer-info" class="inner">
	  &copy; 2019 Storm 
	  - Powered by <a href="javascript:;" target="_blank" rel="external nofollow noopener noreferrer">Hexo</a>
	  - Theme <a href="javascript:;" target="_blank" rel="external nofollow noopener noreferrer">Jane</a>
	</div>
</footer>

    </div>
    <nav id="mobile-nav">
  
    <a href="index.html" class="mobile-nav-link">Home</a>
  
    <a href="index1.html" class="mobile-nav-link">Archives</a>
  
    <a href="index2.html" class="mobile-nav-link">Tag</a>
  
    <a href="javascript:;" class="mobile-nav-link" rel="external nofollow noopener noreferrer" target="_blank">Github</a>
  
</nav>
    
<script>
  var disqus_shortname = 'storm';
  
  var disqus_url = 'https://storm4542.github.io/archives/5983b51f.html';
  
  (function(){
    var dsq = document.createElement('script');
    dsq.type = 'text/javascript';
    dsq.async = true;
    dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
    (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
  })();
</script>


<script src="static/js/jquery.min.js"></script>


  <link rel="stylesheet" href="static/css/jquery.fancybox.css">
  <script src="static/js/jquery.fancybox.pack.js"></script>


<script src="static/js/script.js"></script>

  </div>
</body>
